package com.wyl.rpc.client.proxy;

import com.wyl.rpc.client.RpcClientHandler;
import com.wyl.rpc.client.RpcConnectManager;
import com.wyl.rpc.client.RpcFuture;
import com.wyl.rpc.codec.RpcRequest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author: wyl
 * @date: 2021/8/17
 * @des:
 */
public class RpcProxyImpl<T> implements InvocationHandler, RpcAsyncProxy {

    private Class<T> clazz;

    private long timeout;

    public RpcProxyImpl(Class<T> clazz, long timeout) {
        this.clazz = clazz;
        this.timeout = timeout;
    }

    /**
     * invoke代理接口调用方式
     * <p>
     * JDK 动态代理
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.设置请求对象
        RpcRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString());
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParamterTypes(method.getParameterTypes());
        request.setParamters(args);

        //2.选择一个合适的Client任务处理器
        RpcClientHandler handler = RpcConnectManager.getInstance().chooseHandler();

        //3. 发送真正的客户端请求 返回结果
        RpcFuture future = handler.sendRequest(request);
        return future.get(timeout, TimeUnit.SECONDS);
    }

    /**
     * 异步的代理接口实现, 真正的抱出去RpcFuture 给业务方做实际的回调等待处理
     *
     * @param funcName
     * @param args
     * @return
     */
    @Override
    public RpcFuture call(String funcName, Object... args) {
        //1.设置请求对象
        RpcRequest request = new RpcRequest();
        request.setRequestId(UUID.randomUUID().toString());
        request.setClassName(this.clazz.getName());
        request.setMethodName(funcName);
        request.setParamters(args);
        // 对应的方法参数类型通过 类类型 + 方法名称 通过反射得到parameterTypes
/*        Class<?>[] parameterTypes = getMethodParamTypes(this.clazz, funcName);
        request.setParamterTypes(parameterTypes);*/
        Class<?>[] parameterTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = getClassType(args[i]);
        }
        request.setParamterTypes(parameterTypes);

        //2.选择一个合适的Client任务处理器
        RpcClientHandler handler = RpcConnectManager.getInstance().chooseHandler();
        RpcFuture future = handler.sendRequest(request);
        return future;
    }


    /**
     * 类类型 + 方法名， 获得参数列表类型
     *
     * @param clz
     * @param methodName
     * @return
     */
    public Class[] getMethodParamTypes(Class clz, String methodName) {

        Class[] paramTypes = null;
        //全部方法
        Method[] methods = clz.getMethods();
        for (int i = 0; i < methods.length; i++) {
            //和传入方法名匹配
            if (methodName.equals(methods[i].getName())) {
                Class[] params = methods[i].getParameterTypes();

                paramTypes = new Class[params.length];
                // TODO 方法同名，同参数个数，无法解决
                for (int j = 0; j < params.length; j++) {
                    try {
                        paramTypes[j] = Class.forName(params[j].getName());
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }

                }
                break;
            }
        }
        return paramTypes;
    }

    private Class<?> getClassType(Object obj) {
        Class<?> classType = obj.getClass();
        String typeName = classType.getName();
        if (typeName.equals("java.lang.Integer")) {
            return Integer.TYPE;
        } else if (typeName.equals("java.lang.Long")) {
            return Long.TYPE;
        } else if (typeName.equals("java.lang.Float")) {
            return Float.TYPE;
        } else if (typeName.equals("java.lang.Double")) {
            return Double.TYPE;
        } else if (typeName.equals("java.lang.Character")) {
            return Character.TYPE;
        } else if (typeName.equals("java.lang.Boolean")) {
            return Boolean.TYPE;
        } else if (typeName.equals("java.lang.Short")) {
            return Short.TYPE;
        } else if (typeName.equals("java.lang.Byte")) {
            return Byte.TYPE;
        }
        return classType;
    }
}
